﻿using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

using RayDen.Library.Components.Color.Types;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Interface;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Corrector.Managers;
using RayDen.RayEngine.Data;

namespace RayDen.RayEngine.Corrector.Samplers
{
    public class LightsourceSampler
    {
        private RayEngineScene scene;
        private FastRandom rnd;

        public LightsourceSampler(RayEngineScene scn)
        {
            this.scene = scn;
            this.rnd = new FastRandom();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void EvaluateShadow(ref Point point, ref Normal n, float u0, float u1, float u2, ref LightSample[] result, float ltProb = 0f)
        {

            switch (scene.LightSamplingStrategy)
            {
                case LightSamplingStrategy.UniformOneLight:
                    if (result == null)
                        result = new LightSample[scene.ShadowRaysPerSample];
                    for (int i = 0; i < scene.ShadowRaysPerSample; i++)
                    {
                        int currentLightIndex = scene.SampleLights(ltProb <= 0f ? rnd.NextFloat() : ltProb);
                        var light = scene.Lights[currentLightIndex];
                        var ls = result[i] ?? new LightSample() { LightRay = new RayData() { Org = point } };
                        ls.LightIndex = currentLightIndex;
                        light.SampleSpatialDirect(ref point, ref n, u0, u1, u2, ref ls);
                        ls.LightRay.maxT = ls.Distance;
                        if (light is BaseInfiniteLight)
                        {
                            ls.Color = ColorManager.Instance.Convert(light.Le(ref ls.LightRay.Dir).ToArray());
                        }
                        else
                        {
                            ls.Color = this.SampleProfile(ref ls, light.Profile);
                        }
                        //ls.Pdf *= (scene.ShadowRaysPerSample);
                        result[i] = ls;
                    }
                    break;
                case LightSamplingStrategy.UniformAllLights:
                    {
                        var sm = new List<LightSample>();

                        foreach (var light in scene.Lights)
                        {
                            var ls = new LightSample();
                            light.EvaluateShadow(ref point, ref n, u0, u1, u2, ref ls);
                            if (ls.Pdf > 0f)
                                sm.Add(ls);
                        }
                        result = sm.ToArray();
                    }
                    throw new NotImplementedException("UniformAllLights not implemented");
                    break;
            }
        }

        private IColorType SampleProfile(ref LightSample ls, EmissionProfile profile)
        {
            if (profile.ProfileMode == ProfileMode.EmissionValue)
            {
                return ColorManager.Instance.Convert(profile.EmissionSpectra, SpectrumType.Illuminant);
            }

            var c = (RgbSpectrum)profile.EmissionTexture.Sample(ls.U, ls.V);
            return ColorManager.Instance.Convert(ref c);
        }

    }
}